#!/usr/bin/env python3

import argparse
import json
import logging
import os
import requests
import re
import threading
from datetime import datetime
from concurrent.futures import ThreadPoolExecutor, wait
import subprocess  # 导入 subprocess 用于执行 dpkg 命令


# 全局配置变量
CONFIG = {
    "data_dir": "data",
    "output_dir": ".",
    "proxy": "",
    "thread": 5,
    "category": "",
    "dry_run": False,
}
version_lock = threading.Lock()
# 日志等级，若需要展示每次请求结果请使用 INFO 等级
logging.basicConfig(level=logging.INFO)


# 读取命令行参数
def read_args() -> None:
    parser = argparse.ArgumentParser(
        description="Git Releases Downloader - Git Releases 更新下载器"
    )
    parser.add_argument("-c", "--category", default="", help="分类，用于包的保存路径")
    parser.add_argument("-d", "--data", default="data", help="从 <DATA> 读取仓库配置")
    parser.add_argument(
        "-o", "--output", default=".", help="将文件保存到 <OUTPUT>，默认为当前文件夹"
    )
    parser.add_argument("-p", "--proxy", default="", help="Github 代理")
    parser.add_argument(
        "-t", "--thread", type=int, default=5, help="并发下载线程数量，默认为 5"
    )
    parser.add_argument("--dry-run", action="store_true", help="运行但不下载文件")
    args = parser.parse_args()

    CONFIG.update(
        {
            "category": args.category,
            "data_dir": args.data,
            "output_dir": args.output,
            "proxy": args.proxy,
            "thread": args.thread,
            "dry_run": args.dry_run,
        }
    )


# 读取 JSON 文件
def read_json(filename: str) -> dict:
    try:
        with open(os.path.join(CONFIG["data_dir"], filename), "r") as f:
            return json.load(f)
    except (FileNotFoundError, json.JSONDecodeError) as e:
        logging.error(e)
        return {}


# 获取最新标签
def latest_releases_tag(repo_url: str) -> str:
    url = f"{repo_url}/releases/latest"
    try:
        location = requests.head(url).headers.get("Location", "")
        match = re.search(r".*releases/tag/([^/]+)", location)
        return match.group(1) if match else ""
    except requests.RequestException as e:
        logging.error(e)
        return ""


# 下载文件
def download(url: str, file_path: str) -> tuple[bool, str]:
    # 检查是否为 dry-run 模式
    method = requests.head if CONFIG["dry_run"] else requests.get
    response = method(url, stream=True, allow_redirects=True)
    if response.status_code != 200:
        logging.error(f"Can't download {url} because received {response.status_code}")
        return False, ""

    # 记录文件大小，size_str 使用 kB/MB 而不是 KiB/MiB
    len = int(response.headers.get("Content-Length", 0))
    size_str = f"{len / 10**3:.2f}kB" if len < 10**6 else f"{len / 10**6:.2f}MB"

    if CONFIG["dry_run"]:
        logging.info(f"Dry-run download: {url}")
    else:
        logging.info(f"Downloading: {url}")
        with open(file_path, "wb") as f:
            for chunk in response.iter_content(chunk_size=8192):
                f.write(chunk)
    return True, size_str


# 获取 deb 文件的版本号
def get_deb_version(deb_file: str) -> str:
    try:
        # 使用 dpkg 命令获取版本号
        result = subprocess.run(
            ["dpkg-deb", "-f", deb_file, "Version"],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        return result.stdout.strip()
    except subprocess.CalledProcessError as e:
        logging.error(f"Failed to get version for {deb_file}: {e}")
        return ""


# 检查版本并下载新版本文件
def check(name: str, repo: dict, version_list: dict) -> None:
    if "site" in repo:
        repo_url = os.path.join(repo["site"], repo["repo"])
    else:
        # 默认认为是 GitHub 仓库地址，同时使用代理
        repo_url = os.path.join(CONFIG["proxy"], "https://github.com", repo["repo"])
    releases_tag = latest_releases_tag(repo_url)
    if not releases_tag:
        logging.error(f"Can't get latest releases tag of {name}")
        return
    logging.info(f"{name} = {releases_tag}")

    vpattern = "[0-9][-+.:~a-z0-9A-Z]*"
    version = match.group() if (match := re.search(vpattern, releases_tag)) else ""

    # 判断是否需要更新
    local_version = version_list.get(name, "")
    if not version or local_version == version:
        return

    package_name = repo["package_name"] if "package_name" in repo else name
    for arch, file_template in repo["file_list"].items():
        # 确定本地文件目录并确保目录存在
        app_dir = os.path.join(
            CONFIG["output_dir"], f"{arch}-store", CONFIG["category"], package_name
        )
        os.makedirs(app_dir, exist_ok=True)
        # 拼接得到 Releases 中的文件名
        release_filename = file_template.format(
            releases_tag=releases_tag,  # 若存在则用完整 tag 替换
            version=version,  # 若存在则用 version 替换
        )
        file_url = f"{repo_url}/releases/download/{releases_tag}/{release_filename}"
        file_path = os.path.join(app_dir, release_filename)

        downloaded, size = download(file_url, file_path)
        if downloaded:
            # 获取 deb 文件的版本号
            deb_version = get_deb_version(file_path)
            if not deb_version:
                logging.error(f"Failed to extract version from {file_path}")
                continue

            # 使用 dpkg 获取的版本号进行重命名
            filename = f"{package_name}_{deb_version}_{arch}.deb"  # 使用 dpkg 版本号
            new_file_path = os.path.join(app_dir, filename)
            os.rename(file_path, new_file_path)

            # 判断是否是新添加应用
            if local_version == "":
                print(f"AddNew: {package_name}:{arch} ({deb_version})")
            else:
                print(f"Update: {package_name}:{arch} ({local_version} -> {deb_version})")
                # 删除旧版本文件
                old_file_path = os.path.join(
                    app_dir, f"{package_name}_{local_version}_{arch}.deb"
                )
                if os.path.exists(old_file_path):
                    os.remove(old_file_path)

            # 更新 app.json
            repo["app.json"].update(
                {
                    "Pkgname": package_name,
                    "Version": deb_version,
                    "Filename": filename,
                    "Update": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                    "Size": size,
                    "Contributor": "https://gitee.com/spark-building-service/github",
                }
            )
            # 保存 app.json
            with open(os.path.join(app_dir, "app.json.update"), "w") as f:
                json.dump(repo["app.json"], f, indent=4, ensure_ascii=False)
            # 更新版本号
            with version_lock:
                version_list[name] = deb_version


if __name__ == "__main__":
    read_args()
    git_repo_list = read_json("github.json")
    version_list = read_json("github-local.json")
    with ThreadPoolExecutor(max_workers=CONFIG["thread"]) as executor:
        tasks = [
            executor.submit(check, name, repo, version_list)
            for name, repo in git_repo_list.items()
        ]
        wait(tasks)
    # 保存到 github-local.json
    with open(os.path.join(CONFIG["data_dir"], "github-local.json"), "w") as f:
        json.dump(version_list, f, indent=4)
